package com.nforce.linremote.scripting;

import java.io.IOException;
import java.io.InputStream;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import android.sax.Element;
import android.sax.EndElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.sax.StartElementListener;
import android.util.Xml;

import com.nforce.linremote.model.LArg;
import com.nforce.linremote.model.LButton;
import com.nforce.linremote.model.LButtonArg;
import com.nforce.linremote.model.LFunction;
import com.nforce.linremote.model.LVar;
import com.nforce.linremote.model.Script;

public class ScriptHandler extends DefaultHandler {

    private final Script script;
    private LButton button;
    private LVar variable;
    private String argType;
    private String argTitle;
    private boolean argEditable;
    private String functionName;
    private LFunction function;
    private final boolean loadFull;

    public ScriptHandler(Script script, boolean loadFull) {
	super();
	this.script = script;
	this.loadFull = loadFull;
    }

    public Script parse(InputStream in) {
	RootElement root = new RootElement("program");

	parseFunctions(root);
	parseName(root);

	if (loadFull) {
	    parseVars(root);
	    parseButtons(root);
	}

	try {
	    Xml.parse(in, Xml.Encoding.UTF_8, root.getContentHandler());
	    return script;
	} catch (SAXException e) {
	    // handle the exception
	} catch (IOException e) {
	    // handle the exception
	}
	return null;
    }

    private void parseName(RootElement root) {
	Element name = root.getChild("name");

	name.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		script.setProgramName(LRMLParser.parseFunctions(body, script));
	    }
	});
    }

    private void parseVars(RootElement root) {
	Element var = root.getChild("var");

	var.setStartElementListener(new StartElementListener() {
	    @Override
	    public void start(Attributes attributes) {
		variable = new LVar();
	    }
	});

	var.setEndElementListener(new EndElementListener() {
	    @Override
	    public void end() {
		script.getVariables().put(variable.getName(), variable.getValue());
	    }
	});

	Element vName = var.getChild("name");

	vName.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		variable.setName(body);
	    }
	});

	Element vValue = var.getChild("value");

	vValue.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		variable.setValue(LRMLParser.parseFunctions(body, script));
	    }
	});
    }

    private void parseButtons(RootElement root) {
	Element eButton = root.getChild("button");

	eButton.setStartElementListener(new StartElementListener() {
	    @Override
	    public void start(Attributes attributes) {
		button = new LButton();
	    }
	});

	eButton.setEndElementListener(new EndElementListener() {
	    @Override
	    public void end() {
		script.getButtons().add(button);
	    }
	});

	Element eCommand = eButton.getChild("command");

	eCommand.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		button.setCommand(LRMLParser.parseVars(body, script));
	    }
	});

	Element eImage = eButton.getChild("image");

	eImage.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		button.setImage(LRMLParser.parseVars(body, script));
	    }
	});

	Element eButtonColumn = eButton.getChild("column");

	eButtonColumn.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		button.setColumn(Integer.parseInt(body));
	    }
	});

	Element eButtonRow = eButton.getChild("row");

	eButtonRow.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		button.setRow(Integer.parseInt(body));
	    }
	});

	Element eArgs = eButton.getChild("args");

	Element eArg = eArgs.getChild("arg");

	eArg.setStartElementListener(new StartElementListener() {

	    @Override
	    public void start(Attributes attributes) {
		argType = attributes.getValue("type");
		argTitle = attributes.getValue("title");
		argEditable = Boolean.parseBoolean(attributes.getValue("editable"));
	    }
	});

	eArg.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		Object arg;
		if ("Integer".equals(argType)) {
		    arg = Integer.parseInt(LRMLParser.parseVars(body, script));
		} else {
		    arg = LRMLParser.parseVars(body, script);
		}
		LButtonArg lba = new LButtonArg();
		lba.setType(LRMLParser.parseVars(argType, script));
		lba.setTitle(LRMLParser.parseVars(argTitle, script));
		lba.setEditable(argEditable);
		lba.setArg(arg);
		button.getArgs().add(lba);
	    }
	});
    }

    private void parseFunctions(RootElement root) {
	Element eFunction = root.getChild("function");

	eFunction.setStartElementListener(new StartElementListener() {
	    @Override
	    public void start(Attributes attributes) {
		function = new LFunction();
	    }
	});

	eFunction.setEndElementListener(new EndElementListener() {
	    @Override
	    public void end() {
		script.getFunctions().put(functionName, function);
	    }
	});

	Element eName = eFunction.getChild("name");

	eName.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		functionName = body;
	    }
	});

	Element eCommand = eFunction.getChild("command");

	eCommand.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		function.setCommand(LRMLParser.parseVars(body, script));
	    }
	});

	Element eArgs = eFunction.getChild("args");

	Element eArg = eArgs.getChild("arg");

	eArg.setStartElementListener(new StartElementListener() {

	    @Override
	    public void start(Attributes attributes) {
		argType = attributes.getValue("type");
		argTitle = attributes.getValue("title");
	    }
	});

	eArg.setEndTextElementListener(new EndTextElementListener() {
	    @Override
	    public void end(String body) {
		Object arg;
		if ("Integer".equals(argType)) {
		    arg = Integer.parseInt(LRMLParser.parseVars(body, script));
		} else {
		    arg = LRMLParser.parseVars(body, script);
		}
		LArg lba = new LArg();
		lba.setType(LRMLParser.parseVars(argType, script));
		lba.setTitle(LRMLParser.parseVars(argTitle, script));
		lba.setArg(arg);
		function.getArgs().add(lba);
	    }
	});

	Element eFormat = eFunction.getChild("format");

	eFormat.setEndTextElementListener(new EndTextElementListener() {

	    @Override
	    public void end(String body) {
		function.setFormat(body);
	    }
	});

	Element eResult = eFunction.getChild("result");

	eResult.setEndTextElementListener(new EndTextElementListener() {

	    @Override
	    public void end(String body) {
		function.getResult().add(body);
	    }
	});
    }
}
